﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using NTQOnlineExam.Services.CommandService.Orders;
using NTQOnlineExam.Services.Models;
using PayPal.Payments.Common.Utility;
using PayPal.Payments.DataObjects;
using PayPal.Payments.Transactions;
using PayPal.PayPalAPIInterfaceService;
using PayPal.PayPalAPIInterfaceService.Model;
using PayPal.Permissions.Model;

namespace NTQOnlineExam.Services.CommandService.Payment
{
    public class PaymentCommandService : IPaymentCommandService
    {
        private readonly IOrderCommandService _orderCommandService;

        public PaymentCommandService(IOrderCommandService orderCommandService)
        {
            _orderCommandService = orderCommandService;
        }

        #region Using Payflow

        public bool RequestPaymentByPayFlow(OrderPaymentDTO order, out string message)
        {
            var billingInfo = new BillTo
            {
                BillToFirstName = order.Firstname,
                BillToLastName = order.Lastname,
                BillToStreet = order.Street,
                BillToCity = order.City,
                BillToZip = order.ZipCode,
                BillToState = order.State,
            };

            var invoice = new Invoice
            {
                BillTo = billingInfo,
                InvNum = order.OrderId.ToString(),
                Amt = new Currency(order.SubTotal),
            };

            if (order.Items != null && order.Items.Any())
            {
                foreach (var item in order.Items)
                {
                    invoice.AddLineItem(new LineItem()
                    {
                        SKU = item.ItemId.ToString(),
                        Name = item.Name,
                        Amt = new Currency(item.Price)
                    });
                }

            }

            //var card = new CreditCard(order.CreditCardNo, order.CardExpDate) { Cvv2 = order.CardCVVC };
            //var tender = new CardTender(card);
            var vendor = ConfigurationManager.AppSettings["PaypalVendor"];
            var partner = ConfigurationManager.AppSettings["PaypalPartner"];
            var password = ConfigurationManager.AppSettings["PaypalPassword"];
            var user = ConfigurationManager.AppSettings["PaypalUser"];
            var paypalInTestMode = bool.Parse(ConfigurationManager.AppSettings["PaypalInTestMode"]);
            var vendorInfo = new UserInfo(user, vendor, partner, password);
            var authorizationTransaction = new AuthorizationTransaction(vendorInfo, GetPaypalConnection(paypalInTestMode), invoice, null, PayflowUtility.RequestId);
            authorizationTransaction.Verbosity = "High";
            authorizationTransaction.CreateSecureToken = "Y";
            // The Secure Token Id must be a unique id up to 36 characters.  Using the RequestID object to 
            // generate a random id, but any means to create an id can be used.
            authorizationTransaction.SecureTokenId = PayflowUtility.RequestId;
            var response = authorizationTransaction.SubmitTransaction();
            return ProcessResult(response, paypalInTestMode, out message);

        }
        private bool ProcessResult(Response response, bool isInTestMode, out string message)
        {
            var transactionResponse = response.TransactionResponse;
            if (transactionResponse.Result == 0)
            {
                message = "https://payflowlink.paypal.com?securetoken=" + transactionResponse.SecureToken + "&securetokenid=" + transactionResponse.SecureTokenId + "&MODE=" + (isInTestMode ? "test" : "live");
                return true;
            }
            if (transactionResponse.Result > 0)
            {
                message = string.Format("Result code = {0}, RespMsg = {1}", transactionResponse.Result, transactionResponse.RespMsg);
                return false;
            }
            message = "An internal error occurred.";
            return false;
        }
        private PayflowConnectionData GetPaypalConnection(bool isInTestMode)
        {

            var connection = new PayflowConnectionData(isInTestMode ? "pilot-payflowpro.paypal.com" : "payflowpro.paypal.com", 443, null, 0, null, null);
            return connection;
        }
        #endregion

        #region Using Express Checkout
        public bool RequestPayment(OrderPaymentDTO order, out string message)
        {
            var paypalInTestMode = bool.Parse(ConfigurationManager.AppSettings["PaypalInTestMode"]);
            var configMap = GetPaypalAccountConfigMap(paypalInTestMode);
            var service = new PayPalAPIInterfaceServiceService(configMap);

            #region Build Paypal detail

            var checkoutRequestDetailsType = new SetExpressCheckoutRequestDetailsType();
            var host = ConfigurationManager.AppSettings["HostingEndPoint"];
            string returnHandler = string.Format("{0}{1}", host, "Payment/ProcessSuccessPayment");
            var returnUrl = string.Format("{0}?successUrl={1}&orderId={2}&cancelUrl={3}&total={4}&ccCode={5}", returnHandler, order.SuccessUrl, order.OrderId, order.CancelUrl, order.SubTotal.ToString("0.00", new CultureInfo("en-US")), order.CurrencyCode);
            checkoutRequestDetailsType.ReturnURL = returnUrl;
            checkoutRequestDetailsType.CancelURL = string.Format("{0}{1}", host, order.CancelUrl);

            var lineItems = new List<PaymentDetailsItemType>();
            decimal itemTotals = 0;
            var currencyId = (CurrencyCodeType)Enum.Parse(typeof(CurrencyCodeType), order.CurrencyCode);
            if (order.Items != null && order.Items.Any())
            {
                foreach (var item in order.Items)
                {
                    var pdItemType = new PaymentDetailsItemType
                    {
                        ItemCategory = ItemCategoryType.PHYSICAL,
                        Name = item.Name,
                        Quantity = item.Quantity,
                        Amount = new BasicAmountType(currencyId, (item.Quantity * item.Price).ToString("0.00", new CultureInfo("en-US")))
                    };
                    lineItems.Add(pdItemType);
                    itemTotals += item.Quantity * item.Price;
                }
            }

            var payDetails = new List<PaymentDetailsType>();
            var paydtl = new PaymentDetailsType();
            paydtl.PaymentAction = PaymentActionCodeType.SALE;
            if (order.PaymentFee > 0) paydtl.HandlingTotal = new BasicAmountType(currencyId, order.PaymentFee.ToString("0.00", new CultureInfo("en-US")));
            if (order.TaxTotal > 0) paydtl.TaxTotal = new BasicAmountType(currencyId, order.TaxTotal.ToString("0.00", new CultureInfo("en-US")));
            paydtl.OrderDescription = order.OrderDescription;
            paydtl.OrderTotal = new BasicAmountType(currencyId, order.SubTotal.ToString("0.00", new CultureInfo("en-US")));
            paydtl.PaymentDetailsItem = lineItems;
            if (itemTotals > 0) paydtl.ItemTotal = new BasicAmountType(currencyId, itemTotals.ToString("0.00", new CultureInfo("en-US")));

            payDetails.Add(paydtl);
            checkoutRequestDetailsType.PaymentDetails = payDetails;
            checkoutRequestDetailsType.SolutionType = SolutionTypeType.SOLE;
            checkoutRequestDetailsType.NoShipping = "1";
            checkoutRequestDetailsType.AddressOverride = "0";
            checkoutRequestDetailsType.Address = new AddressType()
            {
                Name = order.Firstname + " " + order.Lastname,
                CityName = order.City,
                PostalCode = order.ZipCode,
                Street1 = order.Street,
                StateOrProvince = order.State,
                Phone = order.Phone
            };
            checkoutRequestDetailsType.BuyerEmail = order.Email;

            var setExpressCheckoutReq = new SetExpressCheckoutRequestType();
            setExpressCheckoutReq.SetExpressCheckoutRequestDetails = checkoutRequestDetailsType;
            var expressCheckoutReq = new SetExpressCheckoutReq();
            expressCheckoutReq.SetExpressCheckoutRequest = setExpressCheckoutReq;
            SetExpressCheckoutResponseType response = null;

            response = service.SetExpressCheckout(expressCheckoutReq);
            if (!response.Ack.ToString().Trim().ToUpper().Equals(AckCode.FAILURE.ToString()) &&
                !response.Ack.ToString().Trim().ToUpper().Equals(AckCode.FAILUREWITHWARNING.ToString()))
            {
                message = string.Format("{0}{1}", ConfigurationManager.AppSettings["PaypalExpressCheckoutUrl"], response.Token);
                return true;
            }

            message = response.Errors[0].ShortMessage;
            return false;

            #endregion
        }

        public bool DoExpressCheckout(DoExpressCheckoutCommand command, out string message)
        {
            message = string.Empty;
            var paypalInTestMode = bool.Parse(ConfigurationManager.AppSettings["PaypalInTestMode"]);
            var configMap = GetPaypalAccountConfigMap(paypalInTestMode);
            var service = new PayPalAPIInterfaceServiceService(configMap);
            var doCheckoutPaymentRequestType = new DoExpressCheckoutPaymentRequestType();
            var expressCheckoutPaymentRequestDetailsType = new DoExpressCheckoutPaymentRequestDetailsType();
            expressCheckoutPaymentRequestDetailsType.Token = command.Token;
            expressCheckoutPaymentRequestDetailsType.PayerID = command.PayerId;
            expressCheckoutPaymentRequestDetailsType.PaymentAction = PaymentActionCodeType.SALE;

            var paymentDetails = new PaymentDetailsType();
            var currencyId = (CurrencyCodeType)Enum.Parse(typeof(CurrencyCodeType), command.Currency);
            paymentDetails.OrderTotal = new BasicAmountType(currencyId, command.Total);

            var payDetailType = new List<PaymentDetailsType> { paymentDetails };
            expressCheckoutPaymentRequestDetailsType.PaymentDetails = payDetailType;
            doCheckoutPaymentRequestType.DoExpressCheckoutPaymentRequestDetails = expressCheckoutPaymentRequestDetailsType;
            var doExpressCheckoutPaymentReq = new DoExpressCheckoutPaymentReq
            {
                DoExpressCheckoutPaymentRequest = doCheckoutPaymentRequestType
            };
            var response = service.DoExpressCheckoutPayment(doExpressCheckoutPaymentReq); ;
            if (!response.Ack.ToString().Trim().ToUpper().Equals(AckCode.FAILURE.ToString()) &&
                !response.Ack.ToString().Trim().ToUpper().Equals(AckCode.FAILUREWITHWARNING.ToString()))
            {
                _orderCommandService.CompleteOrder(new CompleteOrderCommand() { OrderId = new Guid(command.OrderId), DeleteShoppingBasket = true });
                message = command.SuccessUrl + "?orderId=" + command.OrderId;
                return true;
            }
            message = command.CancelUrl;
            return false;
        }

        private Dictionary<string, string> GetPaypalAccountConfigMap(bool inTestMode)
        {
            var config = new Dictionary<string, string>();
            if (inTestMode) config.Add("mode", "sandbox");
            else config.Add("mode", "live");

            config.Add("account1.apiUsername", ConfigurationManager.AppSettings["PaypalAPIUsername"]);
            config.Add("account1.apiPassword", ConfigurationManager.AppSettings["PaypalAPIPassword"]);
            config.Add("account1.apiSignature", ConfigurationManager.AppSettings["PaypalAPISignature"]);
            return config;
        }
        #endregion
    }
}
